<!DOCTYPE html>
<html lang="en">
  <!-- 
    matrix
    
      0 1 2 3 4 5 6 7
    0 0 0 1 5 0 4 5 5
    1 2 2 3 7 2 6 7 7
    2 2 2 3 7 2 6 7 7
    3 0 0 1 5 0 4 5 5
    4 2 2 3 7 2 6 7 7
    5 0 0 1 5 0 4 5 5
    6 0 0 1 5 0 4 5 5
    7 2 2 3 7 2 6 7 7

    Optimization:

      0 1 4 3 6 7 2 5
    0 0 0 0 5 5 5 1 4
    5 0 0 0 5 5 5 1 4
    3 0 0 0 5 5 5 1 4
    6 0 0 0 5 5 5 1 4
    1 2 2 2 7 7 7 3 6
    2 2 2 2 7 7 7 3 6
    4 2 2 2 7 7 7 3 6
    7 2 2 2 7 7 7 3 6
    
  -->
  <head>
    <!-- <meta http-equiv="Content-Type" content="text/html" /> -->
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>3n + 1 Game in Byte</title>
  </head>
  <style>
    :root {
      --FONT-SIZE: 18px;
      --C-WHITE: #f3f3f3;
      --C-BLACK: #333;
      --C-RED: #f99;
      --C-GREEN: #9f9;
      --C-BLUE: #99f;
      --C-GRAY: #666;
    }

    body {
      overflow: scroll;
      background-color: var(--C-BLACK);
      color: var(--C-WHITE);
      font-family: Consolas, "Courier New", monospace;
    }

    p {
      text-align: center;
    }

    #rows {
      display: flex;
      flex-flow: column nowrap;
      /* place-content: start end; */
      place-items: start start;
      width: 100%;
      margin: 0 auto;
      padding: 0;

      .row {
        display: flex;
        min-width: 10em;
        margin: 0.5em 0 0.5em 0.5em;
        flex-flow: row nowrap;
        /* place-items: center center; */
        place-content: center right;
        font-size: var(--FONT-SIZE);

        .box {
          .prev {
            color: var(--C-GREEN);
          }
          .next {
            color: var(--C-BLUE);
          }
          .brow {
            color: var(--C-RED);
          }

          &.cur {
            background-color: var(--C-GRAY);
          }
        }

        &.org {
          background-color: var(--C-WHITE);
          .box {
            color: var(--C-BLACK);
          }
        }
      }
    }
  </style>

  <body>
    <div id="rows"></div>
  </body>

  <script>
    "use strict";

    const MAX_NUM = 1 << 20;
    const GAP_WIDTH = 5;
    const objSum = (obj) => Object.values(obj).reduce((a, b) => a + b, 0);
    const arrNullStr = (arr) => arr.map((e) => (e === null ? "." : e)).join("");
    const rows = document.getElementById("rows");

    const drowPart = (row, part) => {
      for (let i = 0; i < part.n.length; i++) {
        let box = document.createElement("div");
        box.className = "box";

        let prev = document.createElement("div");
        prev.className = "prev";
        prev.textContent = part.p[i] ?? ".";
        box.appendChild(prev);
        let next = document.createElement("div");
        next.className = "next";
        next.textContent = part.n[i] ?? ".";
        box.appendChild(next);
        let brow = document.createElement("div");
        brow.className = "brow";
        brow.textContent = part.b[i] ?? ".";
        box.appendChild(brow);

        row.appendChild(box);
      }
    };

    const drowGap = (row, width = 5, height = 3) => {
      for (let i = 0; i < width; i++) {
        let box = document.createElement("div");
        box.className = "box";

        for (let j = 0; j < height; j++) {
          let gap = document.createElement("div");
          gap.textContent = ".";
          box.appendChild(gap);
        }

        row.appendChild(box);
      }
    };

    const drowBoxRow = (A, B) => {
      let row = document.createElement("div");
      row.className = "row";

      drowPart(row, A);
      drowGap(row, GAP_WIDTH, 3);
      drowPart(row, B);

      rows.appendChild(row);
    };

    const drowNums = (row, nums) => {
      for (let i = 0; i < nums.length; i++) {
        let box = document.createElement("div");
        box.className = "box";

        let num = document.createElement("div");
        num.textContent = nums[i];
        box.appendChild(num);

        row.appendChild(box);
      }
    };

    const drowDexRow = (a, b) => {
      let row = document.createElement("div");
      row.className = "row";

      drowNums(row, a);
      drowGap(row, GAP_WIDTH, 1);
      drowNums(row, b);

      rows.appendChild(row);
    };

    const listOne = ({ n, p, b }) => {
      const l = n.length;

      for (let i = 1, v = 0; i < l; i++) {
        v = n[i] + p[i] + b[i];

        p[i - 1] = n[i];
        b[i - 1] = (v & 2) >> 1;
        n[i] = v & 1;
      }

      return { n, p, b };
    };

    const toOct = ({ n, p, b }) => {
      let rtn = [];
      for (let i = 0; i < n.length; i++) {
        rtn.push(p[i] | (n[i] << 1) | (b[i] << 2));
      }
      return rtn;
    };

    const debugPart = ({ p, n, b }) => `
          ${arrNullStr(p)}
          ${arrNullStr(n)}
          ${arrNullStr(b)}
        `;

    (() => {
      const DSet = new Set();

      for (let i = 0; i < 8; i++) {
        for (let j = 0; j < 8; j++) {
          for (let k = 0; k < 8; k++) {
            let A = {
              p: [0, i & 1, j & 1, k & 1],
              n: [0, (i & 2) >> 1, (j & 2) >> 1, (k & 2) >> 1],
              b: [0, (i & 4) >> 2, (j & 4) >> 2, (k & 4) >> 2],
            };

            let B = listOne({
              p: [...A.p],
              n: [...A.n],
              b: [...A.b],
            });

            let [a, b] = [toOct(A), toOct(B)];

            // console.log( "A:", debugPart(A), "a:", a.join(""), "B:", debugPart(B), "b:", b.join("") );

            DSet.add([a[2], a[3], "-", b[2]].join(""));
            DSet.add([a[1], a[2], "-", b[1]].join(""));
            DSet.add([a[0], a[1], "-", b[0]].join(""));

            drowBoxRow(A, B);
            drowDexRow(a, b);
          }
        }
      }

      console.log("DSet", DSet);

      const patterns = [];
      for (const pair of DSet) {
        let [v, k] = pair.split("-");
        if (!patterns[k]) patterns[k] = [];
        patterns[k].push(v);
      }

      console.log("pattens\n");
      for (let i = 0; i < patterns.length; i++) {
        console.log(i, patterns[i].toString());
      }
     
      /**
       * const matrix = new Array(patterns.length).fill(new Array(patterns.length).fill(0));
       * ！！！ 千万不能这么写 ！！！
       * 这样导致矩阵的每一行都是同一个引用，导致修改一个数组，其他行的数组都修改了
       * 
       */
      const matrix = new Array(patterns.length);
      for (let i = 0; i < matrix.length; i++) {
        matrix[i] = new Array(patterns.length).fill(0);
      }
      Object.defineProperty(matrix, "debug", {
        get: function () {
          let str = "\n  0 1 2 3 4 5 6 7\n";
          for (let i = 0; i < this.length; i++) {
            str += `${i} ${this[i].join(" ")}\n`;
          }
          return str;
        },
      });

      for (let i = 0; i < patterns.length; i++) {
        for (let j = 0; j < patterns[i].length; j++) {
          let [m, n] = patterns[i][j].split("").map((e) => +e);
          matrix[m][n] = i;
        }
      }
      console.log("matrix\n", matrix.debug);
    })();
  </script>
</html>
